/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package idv.cloudee.proxy2.framework;

import idv.cloudee.proxy2.intf.BypassDealer;
import idv.cloudee.proxy2.intf.RequestDealerFactory;
import com.cirnoworks.framework.server.Server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 *
 * @author Cloudee
 */
public class ProxyServer implements Server {

    private int maxConnections = Integer.MAX_VALUE;
    private int port;
    private ServerSocket so;
    private final HashSet<ProxyThread> threads = new HashSet<ProxyThread>();
    private final Object threadsLock = new Object();
    private final ConcurrentHashMap<String, RequestDealerFactory> dealers = new ConcurrentHashMap<String, RequestDealerFactory>();
    private static final ThreadPoolExecutor pool = new ThreadPoolExecutor(10, Integer.MAX_VALUE, 120, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
    private BypassDealer bypass;

    public BypassDealer getBypass() {
        return bypass;
    }

    public void setBypass(BypassDealer bypass) {
        this.bypass = bypass;
    }

    public int getMaxConnections() {
        return maxConnections;
    }

    public void setMaxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
    }

    public int sizeDealers() {
        return dealers.size();
    }

    public RequestDealerFactory putDealer(String key, RequestDealerFactory value) {
        return dealers.put(key, value);
    }

    public RequestDealerFactory getDealer(String key) {
        return dealers.get(key);
    }

    public void clearDealer() {
        dealers.clear();
    }

    public void setPort(int port) {
        this.port = port;
    }

    public ProxyServer(int port) {
        this.port = port;
    }

    public void onConnectionClosed(ProxyThread thread) {
        synchronized (threadsLock) {
            threads.remove(thread);
            threadsLock.notifyAll();
        }
    }

    public static void submitThread(Runnable run) {
        pool.execute(run);
    }

    public void prepareSync() {
        try {
            so = new ServerSocket(port);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public void prepareASync() {
    }

    public void mainLoop() {
        try {
            synchronized (threadsLock) {
                while (threads.size() >= maxConnections) {
                    threadsLock.wait();
                }
            }
            Socket s = so.accept();
            ProxyThread t = new ProxyThread(s, this, bypass);
            synchronized (threadsLock) {
                threads.add(t);
            }
            t.start();
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    public void cleanup() {
        try {
            so.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        synchronized (threadsLock) {
            for (ProxyThread t : threads) {
                t.close();
            }
        }
    }

    public void forceShutdown() {
        cleanup();
    }

    public boolean isShutdown() {
        synchronized (threadsLock) {
            return threads.isEmpty();
        }
    }

    public int countThreads() {
        return threads.size();
    }

    public int countThreads2() {
        return pool.getActiveCount();
    }

    public int countPool() {
        return pool.getPoolSize();
    }

    public int countMax() {
        return pool.getLargestPoolSize();
    }
}
